package com.demo.api.filter;

import com.alibaba.fastjson.JSONObject;
import com.demo.api.cache.DeptCache;
import com.demo.api.cache.UrlCache;
import com.demo.api.interceptor.user.AuthUserInfo;
import com.demo.api.interceptor.user.UserInfo;
import com.demo.client.model.user.SysInterface;
import com.demo.client.model.user.SysUser;
import com.demo.api.service.logs.ISysLogService;
import com.demo.api.service.user.IUserService;
import com.demo.core.constant.CommonKey;
import com.demo.core.entity.AuthUser;
import com.demo.core.enums.ErrorEnum;
import com.demo.core.enums.FilterEnum;
import com.demo.core.enums.OftenEnum;
import com.demo.core.enums.RedisCacheEnum;
import com.demo.core.redis.RedisCache;
import com.demo.core.redis.RedisTableCache;
import com.demo.core.response.ApiResponse;
import com.demo.core.utils.StringUtils;
import com.demo.core.utils.TokenTools;
import com.demo.core.verification.ValidatorTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Tan-Bowen
 * @version 1.0
 * @description TODO
 * @date 2021/7/28 10:05
 */
@Slf4j
@Component
public class AuthFilter implements Filter {

    @Resource
    private TokenTools tokenTools;
    @Resource
    private RedisCache redisCache;
    @Resource
    private UrlCache urlCache;
    @Resource
    private IUserService userService;
    @Resource
    private RedisTableCache redisTableCache;
    @Resource
    private DeptCache deptCache;

    @Resource
    private ISysLogService logService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        FilterEnum filter = urlFilter(request);
        if (filter.equals(FilterEnum.OPEN)) {
            filterChain.doFilter(request, response);
            return;
        }
        String uri = request.getRequestURI();
        if (urlCache.get(uri) == null) {
            setUnauthorizedResponse(request, response, ErrorEnum.NO_AUTHORIZATION);
            return;
        }
        SysInterface sysInterface = urlCache.get(uri);
        // 不检查权限的跳开
        if (!sysInterface.getToken()) {
            filterChain.doFilter(request, response);
            return;
        }
        // 获取 token
        String authToken = request.getHeader(CommonKey.AUTHORIZATION);
        if (StringUtils.isEmpty(authToken)) {
            setUnauthorizedResponse(request, response, ErrorEnum.TOKEN_EXPIRED);
            return;
        }
        if (StringUtils.startsWith(authToken, CommonKey.BEARER)) {
            authToken = StringUtils.replace(authToken, CommonKey.BEARER, "");
        }
        try {
            if (!this.tokenTools.validateToken(authToken)) {
                setUnauthorizedResponse(request, response, ErrorEnum.TOKEN_EXPIRED);
                return;
            }
        } catch (Throwable e) {
            setUnauthorizedResponse(request, response, ErrorEnum.TOKEN_EXPIRED);
            log.error("无效的token:{},{}", authToken, e);
            return;
        }
        AuthUser user = this.tokenTools.authUser(authToken);
        Long userId = user.getUserId();
        if (!checkOnlyOne(user, authToken)) {
            setUnauthorizedResponse(request, response, ErrorEnum.ONLY_ONE_DEVICES);
            return;
        }
        SysUser sysUser = redisTableCache.get(SysUser.class, user.getUserId());
        if (sysUser == null) {
            setUnauthorizedResponse(request, response, ErrorEnum.USER_INFO_FAIL);
            return;
        }
        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(request);
        requestWrapper.addHeader(CommonKey.USER_ID, userId + "");
        // 控制权限
        if (sysInterface.getAuth()) {
            List<String> auth = userService.getMenuAndAuth(userId).getUrls();
            if (auth == null || auth.isEmpty() || !auth.contains(uri)) {
                setUnauthorizedResponse(request, response, ErrorEnum.ACCESS_DENIED);
                return;
            }
        }
        // 设置全局用户信息
        this.setUserInfo(sysUser);
        filterChain.doFilter(requestWrapper, response);
    }

//    @Override
//    public void destroy() {
//    }

    private FilterEnum urlFilter(HttpServletRequest request) {
        String uri = request.getRequestURI();
        if (StringUtils.indexOf(uri, CommonKey.SWAGGER_API) != -1) {
            return FilterEnum.OPEN;
        }
        //如果是swagger的uri,就默认开放
        if (StringUtils.indexOf(uri, CommonKey.SWAGGER) != -1) {
            return FilterEnum.OPEN;
        }
        if (StringUtils.indexOf(uri, CommonKey.FAVICON) != -1) {
            return FilterEnum.OPEN;
        }
        return FilterEnum.API;
    }


    private void setUnauthorizedResponse(HttpServletRequest request, HttpServletResponse response, ErrorEnum errorEnum) throws IOException {
        try {
            logService.createErrorLog(request, errorEnum,"");
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(JSONObject.toJSONString(ApiResponse.builder().code(errorEnum.getCode()).msg(errorEnum.getMsg()).build()));
        } catch (Throwable e) {
            log.error("返回数据错误:{}", e);
        }
    }

    private boolean checkOnlyOne(AuthUser user, String authToken) {
        if (!user.isOnlyOne()) {
            return true;
        }
        //需要校验是否是单设备登录
        String tokenTemp = redisCache.getJedis().opsForValue().get(RedisCacheEnum.USER_TOKEN.createKey(user.getUserId()));
        //没有缓存，不处理接下来的逻辑
        if (StringUtils.isEmpty(tokenTemp)) {
            return true;
        }
        //缓存的token是否有效
        boolean isValidate = tokenTools.validateToken(tokenTemp);
        if (!isValidate) {
            //如果无效就不校验了
            return true;
        }
        //如果有效，判断一下两个token是否一样
        //两个token一样，说明是同一用户在访问
        return StringUtils.equals(authToken, tokenTemp);
        //有人用老的token在请求数据，打回去
    }


    public void setUserInfo(SysUser user) {
        List<Long> childes = new ArrayList<>();
        // 如过是超级管理员那么就没有部门
        if (!ValidatorTools.isSuper(user.getDeptId())) {
            childes = deptCache.getChildren(user.getDeptId());
            childes.add(user.getDeptId());
            if (childes.size() == 0) childes.add(Long.valueOf(OftenEnum.Super.DEFAULT.code()));
        }
        UserInfo userInfo = UserInfo.builder()
                .userId(user.getId())
                .userName(user.getUserName())
                .deptId(user.getDeptId())
                .deptIds(childes)
                .build();
        AuthUserInfo.setUserInfo(userInfo);
    }
}
